import {useEffect, useRef} from 'react'
import './index.css'

const Scroll = props => {
  const {scroll, children, style, className} = props

  const scrollRef = useRef(null)

  useEffect(() => initScroll())

  const initScroll = () => {
    const {current} = scrollRef
    const element = current.querySelector('.scroll-content')
    const scrollbarY = current.querySelector('.scroll-y')
    const sliderY = scrollbarY.querySelector('.scroll-slider')
    const scrollbarX = current.querySelector('.scroll-x')
    const sliderX = scrollbarX.querySelector('.scroll-slider')

    const offsetHeight = element.offsetHeight       //视口高度
    const scrollHeight = element.scrollHeight       //内容高度
    const offsetWidth = element.offsetWidth         //视口宽度
    const scrollWidth = element.scrollWidth         //内容宽度
    const scrollbarYWidth = sliderY.clientWidth     //纵向滚动条宽度
    const scrollbarXHeight = sliderX.clientHeight   //横向滚动条高度

    if (scrollHeight <= offsetHeight) {
      scrollbarY.style.display = 'none'
    } else {
      sliderY.style.height = offsetHeight * offsetHeight / scrollHeight + 'px'
    }

    if (scrollWidth <= offsetWidth) {
      scrollbarX.style.display = 'none'
    } else {
      sliderX.style.width = offsetWidth * offsetWidth / scrollWidth + 'px'
    }

    const scrollArguments = {element, sliderY, scrollHeight, offsetHeight, sliderX, scrollWidth, offsetWidth, scrollbarYWidth, scrollbarXHeight}

    element.addEventListener('scroll', () => onScroll(scrollArguments))
    sliderY.addEventListener('mousedown', e => onDrag(e, scrollArguments, 'scrollY'), false)
    sliderX.addEventListener('mousedown', e => onDrag(e, scrollArguments, 'scrollX'), false)

    if (props.mode === 'auto') {
      showScrollBar(scrollArguments, 0)
      scrollbarY.addEventListener('mouseenter', () => showScrollBar(scrollArguments, 1))
      scrollbarX.addEventListener('mouseenter', () => showScrollBar(scrollArguments, 1))
      scrollbarY.addEventListener('mouseleave', () => setTimeout(() => { showScrollBar(scrollArguments, 0) }, 2000))
      scrollbarX.addEventListener('mouseleave', () => setTimeout(() => { showScrollBar(scrollArguments, 0) }, 2000))
    }
  }

  const onScroll = scrollArguments => {
    const {element, sliderY, scrollHeight, offsetHeight, sliderX, scrollWidth, offsetWidth, scrollbarYWidth, scrollbarXHeight} = scrollArguments
    const scrollTop = element.scrollTop
    const sliderYHeight = sliderY.getBoundingClientRect().height
    let ratioY = scrollTop / (scrollHeight - offsetHeight)
    const scrollLeft = element.scrollLeft
    const sliderXWidth = sliderX.getBoundingClientRect().width
    let ratioX = scrollLeft / (scrollWidth - offsetWidth)

    if (ratioY > 1) ratioY = 1
    if (ratioX > 1) ratioX = 1

    if (props.mode === 'auto') {
      showScrollBar(scrollArguments, 1)

      setTimeout(() => {
        const currentScrollTop = element.scrollTop
        currentScrollTop === scrollTop && showScrollBar(scrollArguments, 0)
      }, 2000)
    }

    sliderY.style.top = `${ratioY * (offsetHeight - sliderYHeight - scrollbarYWidth)}px`
    sliderX.style.left = `${ratioX * (offsetWidth - sliderXWidth - scrollbarXHeight)}px`
  }

  const onDrag = (downEvent, dragArguments, direction) => {
    const {element, scrollHeight, scrollWidth, sliderY, sliderX, offsetHeight, offsetWidth} = dragArguments

    const sliderYHeight = sliderY.getBoundingClientRect().height
    const elementTop = element.getBoundingClientRect().top
    const originTop = sliderY.getBoundingClientRect().top - elementTop
    const maxYDistance = offsetHeight - sliderYHeight
    let originY = downEvent.clientY

    const sliderXWidth = sliderX.getBoundingClientRect().width
    const elementLeft = element.getBoundingClientRect().left
    const originLeft = sliderX.getBoundingClientRect().left - elementLeft
    const maxXDistance = offsetWidth - sliderXWidth
    let originX = downEvent.clientX

    document.onmousemove = moveEvent => {

      if (direction === 'scrollY') {
        element.scrollTop = (moveEvent.clientY - originY + originTop) * scrollHeight / offsetHeight
        parseInt(sliderY.style.top) <= 0 && (sliderY.style.top = '0px')
        parseInt(sliderY.style.top) >= maxYDistance && (sliderY.style.top = `${maxYDistance}px`)
      } else {
        element.scrollLeft = (moveEvent.clientX - originX + originLeft) * scrollWidth / offsetWidth
        parseInt(sliderX.style.left) <= 0 && (sliderX.style.left = '0px')
        parseInt(sliderX.style.left) >= maxXDistance && (sliderX.style.left = `${maxXDistance}px`)
      }
    }

    document.onmouseup = () => {
      document.onmousemove = null
      document.onmouseup = null
    }
  }

  const showScrollBar = (sliderElement, value) => {
    const {sliderY, sliderX} = sliderElement
    sliderY.style.opacity = value
    sliderX.style.opacity = value
  }

  return (
    <div className="scroll-container" ref={scrollRef}>
      <div className={`scroll-content ${className}`} style={style}>{children}</div>

      <div className={`scrollbar scroll-y ${scroll === 'x' ? 'scroll-disabled' : ''}`}>
        <div className="scroll-slider"></div>
      </div>

      <div className={`scrollbar scroll-x ${scroll === 'y' ? 'scroll-disabled' : ''}`}>
        <div className="scroll-slider"></div>
      </div>
    </div>
  )
}

export default Scroll